class Key:
	"""Container class for keyboard constants.

	There are generally no constants for keys having a one-character
	ASCII representation.

	The keyboard constants are currently strings, but this might change
	in the future.
	"""

	# Movement: it's likely that there are dedicated
	#  keys only for the cardinal directions, but other
	#  keys can be translated into these constants
	#  later, during interpretation.
	East, Northeast = "east", "northeast"
	North, Northwest = "north", "northwest"
	West, Southwest = "west", "southwest"
	South, Southeast = "south", "southeast"
	Middle = "middle"

	Up = North
	Down = South
	Left = West
	Right = East

	Home = "home"
	PageDown = "page down"
	PageUp = "page up"
	End = "end"

class KeyboardInput:
	"""A keyboard input event.

	This is a generic representation of keyboard input which should
	be produced by interface-specific code and handed over to
	interface-independent code.

	Instances of this class include a member identifying them as such:
		.type		"keyboard"
	This is to distinguish keyboard input events from e.g. mouse input
	events. 

	The keystroke is classified according to the following members:
		.ascii		An integer in the range [0,255], if and only
				if the keystroke represents an ASCII character
				If not, ascii is None.
		.desc		A string representing the character,
				either being a one-character string
				consisting of an ASCII character(if and only
				if .ascii is not None), or corresponding to
				a constant in Key. If no such constant exists,
				.desc is None.
	Only keystrokes deemed relevant by the subclass implementation are
	given such description; others are represented by .ascii = None,
	.desc = None and should be ignored.

	Several different keystrokes may be mapped to the same character, and
	KeyboardInput events may be generated artificially to trigger the
	effects of a particular keypress.

	Meta keys are not handled beyond distinguishing between lowercase
	and uppercase letters.
	"""
	def __init__(self, ascii = None, desc = None, shifted = False):
		self.shifted = shifted
		self.ascii = ascii
		self.desc = desc
		self.type = "keyboard"
		if ascii != None and desc != None:
			assert desc == chr(ascii)
		if ascii != None and desc == None:
			self.desc = chr(ascii)

class KeyboardInterpreter:
	"""Interprets keyboard events in ways useful to a roguelike.

	Implementations may wish to subclass this to neatly interpret
	non-generic keys. The relevant member is
		.ki
	which is an instance of KeyboardInput.
	"""
	def __init__(self, keyboardInput):
		self.ki = keyboardInput
	def asNumpadVector(self):
		if self.ki.ascii == ord('8') or self.ki.desc == Key.North: return (0,-1)
		if self.ki.ascii == ord('2') or self.ki.desc == Key.South: return (0,1)
		if self.ki.ascii == ord('4') or self.ki.desc == Key.West: return (-1,0)
		if self.ki.ascii == ord('6') or self.ki.desc == Key.East: return (1,0)
		if self.ki.ascii == ord('7') or self.ki.desc == Key.Home or self.ki.desc == Key.Northwest: return (-1,-1)
		if self.ki.ascii == ord('9') or self.ki.desc == Key.PageUp or self.ki.desc == Key.Northeast: return (1,-1)
		if self.ki.ascii == ord('1') or self.ki.desc == Key.End or self.ki.desc == Key.Southwest: return (-1,1)
		if self.ki.ascii == ord('3') or self.ki.desc == Key.PageDown or self.ki.desc == Key.Southeast: return (1,1)
		return None
	def asCardinalVector(self):
		if self.ki.desc == Key.North: return (0,-1)
		if self.ki.desc == Key.South: return (0,1)
		if self.ki.desc == Key.West: return (-1,0)
		if self.ki.desc == Key.East: return (1,0)
		return None
	def asViVector(self):
		if self.ki.ascii == ord('k'): return (0,-1)
		if self.ki.ascii == ord('j'): return (0,1)
		if self.ki.ascii == ord('h'): return (-1,0)
		if self.ki.ascii == ord('l'): return (1,0)
		if self.ki.ascii == ord('y'): return (-1,-1)
		if self.ki.ascii == ord('u'): return (1,-1)
		if self.ki.ascii == ord('b'): return (-1,1)
		if self.ki.ascii == ord('n'): return (1,1)
		return None
	def asUppercaseViVector(self):
		if self.ki.ascii == ord('K'): return (0,-1)
		if self.ki.ascii == ord('J'): return (0,1)
		if self.ki.ascii == ord('H'): return (-1,0)
		if self.ki.ascii == ord('L'): return (1,0)
		if self.ki.ascii == ord('Y'): return (-1,-1)
		if self.ki.ascii == ord('U'): return (1,-1)
		if self.ki.ascii == ord('B'): return (-1,1)
		if self.ki.ascii == ord('N'): return (1,1)
		return None
	def isAscii(self, letter, ignoreCase = False):
		if ignoreCase:
			return self.isAscii( letter ) or self.isAscii( letter.swapcase())
		return self.ki.ascii != None and self.ki.ascii == ord(letter)
	def asText(self, allowed = None):
		if self.ki.ascii == None: return None
		if self.ki.ascii >= 32 and self.ki.ascii < 127:
			c = chr( self.ki.ascii )
			if not allowed or c in allowed:
				return c
		return None
	def isEnter(self):
		return self.isAscii( '\n' ) or self.isAscii( '\r' )
	def isBackspace(self):
		return self.isAscii( chr(8) )
	def isShifted(self): # erl extension
		return self.ki.shifted
	def isNumpadCenter(self):
		return self.ki.ascii == ord('5') or self.ki.desc == Key.Middle
